#include <QInputDialog>

// from WccApi
#include "wccapi/wccfnlogin.h"
#include "wccapi/wccgetrequest.h"
#include "wccapi/wccnetworkaccessmanager.h"
#include "wccapi/wccsigninrequest.h"

// from this project
#include "generatoroutputview.h"
#include "rcsession.h"
#include "settings.h"

namespace
{
    const char * const SERVER_URL     =   "https://build.forum.nokia.com";
    const char * const SSO_LOGIN_URL  =   "https://sso.forum.nokia.com/login?";
    const char * const SSO_LOGOUT_URL =   "https://sso.forum.nokia.com/logout";
}

RcSession::RcSession(GeneratorOutputView & outputView,
                     QObject             * parent = NULL)
    : m_outputView(outputView)
    , m_networkAccessManager(NULL)
    , m_currentReqId(0)
{
    setParent(parent);
    m_networkAccessManager = new WccApi::WccNetworkAccessManager(this);

    loadSettings();
}

RcSession::~RcSession()
{
    ;
}

bool RcSession::isSignedOn() const
{
    return m_sessionIdCookie.length() > 0;
}

void RcSession::signIn()
{
    if (isSignedOn())
    {
        emit signInCompleted(true);
        return;
    }

    loadSettings();

    if (m_proxyConfig == Settings::PCK_UndefinedProxyConfig)
    {
        m_outputView.printOutput("Unconfigured proxy settings");
        emit signInCompleted(false);
        return;
    }

    int
        id = nextReqId();
    QString
        geturl = QString(SSO_LOGIN_URL)
                 + "service="
                 + QUrl::toPercentEncoding( QString(SERVER_URL)
                                            + "/api/sign-in?cas=fn");

    m_networkAccessManager->ssoFormForumNokia(id, geturl);
    WccApi::WccSsoFormForumNokia
        *req = qobject_cast<WccApi::WccSsoFormForumNokia*>(m_networkAccessManager->requestById(id));

    if (!req)
    {
        m_outputView.printOutput("Could not get response to Forum Nokia SSO request");
        emit signInCompleted(false);
        return;
    }

    connect(req,
            SIGNAL(finished()),
            this,
            SLOT(ssoFormReplySlot()));
}

void RcSession::signOut()
{
    int
        id = nextReqId();
    m_networkAccessManager->get(id,
                                SSO_LOGOUT_URL);
    m_sessionIdCookie = QByteArray();
    m_currentReqId = 0;
}

void RcSession::refreshRcProperties()
{
    int
        id = nextReqId();

    m_networkAccessManager->get(id,
                                makeRequestUrl("properties")); //?cl=nqsc&v=1"));

    WccApi::WccGetRequest
        *req = qobject_cast<WccApi::WccGetRequest*>(m_networkAccessManager->requestById(id));

    if (!req)
    {
        m_outputView.printOutput("Could not get remote build target properties");
        emit refreshRcPropertiesCompleted(false,
                                          false);
        return;
    }

    connect(req,
            SIGNAL(finished()),
            this,
            SLOT(propertiesReplySlot()));
}

QString RcSession::makeRequestUrl(const QString & operation)
{
    return QString(SERVER_URL) + "/api/rpc/" + operation;
}

WccApi::WccNetworkAccessManager * RcSession::networkAccessManager()
{
    return m_networkAccessManager;
}

int RcSession::nextReqId()
{
    return ++m_currentReqId;
}

void RcSession::ssoFormReplySlot()
{
    QObject *oreq = sender();
    WccApi::WccSsoFormForumNokia
        *req = qobject_cast<WccApi::WccSsoFormForumNokia*>(oreq);
    if (!req)
    {
        m_outputView.printOutput("Could not obtain Nokia Form SSO response");
        emit signInCompleted(false);
        return;
    }

    WccApi::WccResponse
        *resp = req->response();
    m_outputView.printOutput("SSO login form:");
    m_outputView.printOutput(resp->message());

    QString
        specid = req->m_forumNokiaSSOSpecialId;

    QString
        forumNokiaPassword = QInputDialog::getText(qobject_cast<QWidget*>(this->parent()),
                                                   "Login to Remote Compiler (Forum Nokia)",
                                                   "Forum Nokia password",
                                                   QLineEdit::Password);

    if (forumNokiaPassword.length() == 0)
    {
        m_outputView.printOutput("No password given");
        emit signInCompleted(false);
        return;
    }

    QString
        srvurl = QString::fromAscii(QUrl::toPercentEncoding( QString(SERVER_URL)
                                                             + "/api/sign-in?cas=fn"));
    QString reqargs = QString("service=")
                      + srvurl
                      + "&lt="
                      + specid
                      + "&_currentStateId=&_eventId=submit&username="
                      + m_forumNokiaUserName
                      + "&password="
                      + forumNokiaPassword;
    int id = nextReqId();
    QString
        url = QString(SSO_LOGIN_URL) + reqargs;
    m_networkAccessManager->ssoAuthForumNokia(id,
                                              url,
                                              QString());
    WccApi::WccSsoAuthForumNokia
        *nreq = qobject_cast<WccApi::WccSsoAuthForumNokia*>(m_networkAccessManager->requestById(id));

    if (!nreq)
    {
        m_outputView.printOutput("Could not obtain Nokia Form SSO auth response");
        emit signInCompleted(false);
        return;
    }

    connect(nreq,
            SIGNAL(finished()),
            this,
            SLOT(ssoAuthReplySlot()));
}

void RcSession::ssoAuthReplySlot()
{
    QObject
        *oreq = sender();
    WccApi::WccSsoAuthForumNokia
        *req = qobject_cast<WccApi::WccSsoAuthForumNokia*>(oreq);
    if (!req)
    {
        m_outputView.printOutput("Could not obtain Nokia Form SSO auth response");
        emit signInCompleted(false);
        return;
    }

    WccApi::WccResponse
        *resp = req->response();
    m_outputView.printOutput("SSO auth response:");
    m_outputView.printOutput(resp->message());

    QString
        loc = req->m_location;
    int
        id = nextReqId();
    // no session id yet
    m_networkAccessManager->signIn(id,
                                   loc,
                                   "");
    WccApi::WccSignInRequest
        *nreq = qobject_cast<WccApi::WccSignInRequest*>(m_networkAccessManager->requestById(id));

    if (!nreq)
    {
        m_outputView.printOutput("Could not obtain Nokia Form signin request");
        emit signInCompleted(false);
        return;
    }

    connect(nreq,
            SIGNAL(finished()),
            this,
            SLOT(ssoTicketSendingFinishedSlot()));
}

void RcSession::ssoTicketSendingFinishedSlot()
{
    QObject
        *oreq = sender();
    WccApi::WccSignInRequest
        *req = qobject_cast<WccApi::WccSignInRequest*>(oreq);
    if (!req)
    {
        m_outputView.printOutput("Could not obtain Nokia Form signin request");
        emit signInCompleted(false);
        return;
    }

    WccApi::WccResponse
        *resp = req->response();
    m_outputView.printOutput("SSO Response to sign in request:");
    m_outputView.printOutput(resp->message());

    QByteArray
        sessionIdCookie = req->m_cookie;
    setSessionIdCookie(sessionIdCookie);

    emit signInCompleted(sessionIdCookie.length() > 0);
}

void RcSession::propertiesReplySlot()
{
    QObject
        *oreq = sender();
    WccApi::WccGetRequest
        *req = qobject_cast<WccApi::WccGetRequest*>(oreq);

    if (!req)
    {
        m_outputView.printOutput("Could not obtain remote build target properties request");
        emit refreshRcPropertiesCompleted(false,
                                          false);
        return;
    }

    WccApi::WccResponse
        *resp = req->response();
    QString
        newRcProperties = resp->message(),
        oldRcProperties = Settings::get(Settings::RcProperties).toString();

    bool
       hasRcPropertiesChanged = newRcProperties != oldRcProperties;

    if (hasRcPropertiesChanged)
    {
        Settings::set(Settings::RcProperties,
                      newRcProperties);
    }

    emit refreshRcPropertiesCompleted(newRcProperties.length() > 0,
                                      hasRcPropertiesChanged);
}

void RcSession::loadSettings()
{
    m_forumNokiaUserName = Settings::get(Settings::ForumNokiaUserName).toString();
    m_proxyConfig = static_cast<Settings::ProxyConfigKind>(Settings::get(Settings::ProxyConfig).toInt());
    m_proxyUrl = Settings::get(Settings::ProxyUrl).toString();
    m_proxyPort = Settings::get(Settings::ProxyPort).toInt();
    m_proxyUserName = Settings::get(Settings::ProxyUserName).toString();
    m_proxyPassword = Settings::get(Settings::ProxyPassword).toString();

    switch (m_proxyConfig)
    {
    case Settings::PCK_NoProxy:
        m_networkAccessManager->setNoProxy();
        break;
    case Settings::PCK_SystemProxy:
        m_networkAccessManager->setSystemProxy();
        break;
    case Settings::PCK_ManualProxy:
        m_networkAccessManager->setProxy(m_proxyUrl,
                                         m_proxyPort,
                                         m_proxyUserName,
                                         m_proxyPassword);
        break;
    default:
        ;
        break;
    }
}

void RcSession::setSessionIdCookie(const QByteArray & sessionIdCookie)
{
    m_sessionIdCookie = sessionIdCookie;
}
